/*************************************************
	作者: Plane
	邮箱: 1785275942@qq.com
	日期: 2021/03/05 2:02
	功能: 毫秒级精确的定时器

    //=================*=================\\
           教学官网：www.qiqiker.com
           关注微信服务号: qiqikertuts
           关注微信公众号: PlaneZhong
               ~~获取更多教学资讯~~
    \\=================*=================//
*************************************************/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace PETimer {
    public class TickTimer : PETimer {
        class TickTaskPack {
            public int tid;
            public Action<int> cb;
            public TickTaskPack(int tid, Action<int> cb) {
                this.tid = tid;
                this.cb = cb;
            }
        }

        private readonly DateTime startDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
        private readonly ConcurrentDictionary<int, TickTask> taskDic;
        private readonly bool setHandle;
        private readonly ConcurrentQueue<TickTaskPack> packQue;
        private const string tidLock = "TickTimer_tidLock";

        private readonly Thread timerThread;
        private readonly CancellationTokenSource threadCts; // 用于优雅地停止线程
        public TickTimer(int interval = 0, bool setHandle = true) {
            taskDic = new ConcurrentDictionary<int, TickTask>();
            this.setHandle = setHandle;
            threadCts = new CancellationTokenSource();
            if(setHandle) {
                packQue = new ConcurrentQueue<TickTaskPack>();
            }
            if(interval != 0) {
                void StartTick() {
                    try {
                        while(!threadCts.Token.IsCancellationRequested) {
                            UpdateTask();
                            Thread.Sleep(interval);
                        }
                    }
                    catch(OperationCanceledException) {
                        // 正常取消，忽略
                    }
                    catch(Exception e) {
                        ErrorFunc?.Invoke($"Tick Thread Error: {e}.");
                    }
                }
                timerThread = new Thread(new ThreadStart(StartTick)) {
                    IsBackground = true // 设置为后台线程
                };
                timerThread.Start();
            }
        }

        public override int AddTask(uint delay, Action<int> taskCB, Action<int> cancelCB, int count = 1) {
            int tid = GenerateTid();
            double startTime = GetUTCMilliseconds();
            double destTime = startTime + delay;
            TickTask task = new TickTask(tid, delay, count, destTime, taskCB, cancelCB, startTime);
            if(taskDic.TryAdd(tid, task)) {
                return tid;
            }
            else {
                WarnFunc?.Invoke($"key:{tid} already exist.");
                return -1;
            }
        }
        public override bool DeleteTask(int tid) {
            if(taskDic.TryRemove(tid, out TickTask task)) {
                if(setHandle && task.cancelCB != null) {
                    packQue.Enqueue(new TickTaskPack(tid, task.cancelCB));
                }
                else {
                    task.cancelCB?.Invoke(tid);
                }
                return true;
            }
            else {
                WarnFunc?.Invoke($"tid:{tid} remove failed.");
                return false;
            }
        }
        public override void Reset() {
            if(packQue != null && !packQue.IsEmpty) {
                WarnFunc?.Invoke("Callback Queue is not Empty.");
            }

            // 取消所有任务
            foreach(var kvp in taskDic) {
                // TickTask 没有取消标记，直接清理即可
            }

            taskDic.Clear();
            
            // 使用 CancellationToken 优雅地停止线程，替代 Thread.Abort()
            if(timerThread != null && timerThread.IsAlive) {
                threadCts?.Cancel();
                // 等待线程结束（最多等待1秒）
                if(!timerThread.Join(1000)) {
                    WarnFunc?.Invoke("Timer thread did not stop gracefully.");
                }
            }
        }
        public void UpdateTask() {
            double nowTime = GetUTCMilliseconds();
            foreach(var item in taskDic) {
                TickTask task = item.Value;
                if(nowTime < task.destTime) {
                    continue;
                }

                ++task.loopIndex;
                if(task.count > 0) {
                    --task.count;
                    if(task.count == 0) {
                        FinsisTask(task.tid);
                    }
                    else {
                        task.destTime = task.startTime + task.delay * (task.loopIndex + 1);
                        CallTaskCB(task.tid, task.taskCB);
                    }
                }
                else {
                    task.destTime = task.startTime + task.delay * (task.loopIndex + 1);
                    CallTaskCB(task.tid, task.taskCB);
                }
            }
        }
        public void HandleTask() {
            while(packQue != null && packQue.Count > 0) {
                if(packQue.TryDequeue(out TickTaskPack pack)) {
                    pack.cb.Invoke(pack.tid);
                }
                else {
                    ErrorFunc?.Invoke("packQue Dequeue Data Error.");
                }
            }
        }

        void FinsisTask(int tid) {
            //线程安全字典，遍历过程中删除无影响。
            if(taskDic.TryRemove(tid, out TickTask task)) {
                CallTaskCB(tid, task.taskCB);
                task.taskCB = null;
            }
            else {
                WarnFunc?.Invoke($"Remove tid:{tid} task in Dic Failed.");
            }
        }
        void CallTaskCB(int tid, Action<int> taskCB) {
            if(setHandle) {
                packQue.Enqueue(new TickTaskPack(tid, taskCB));
            }
            else {
                taskCB.Invoke(tid);
            }
        }
        private double GetUTCMilliseconds() {
            TimeSpan ts = DateTime.UtcNow - startDateTime;
            return ts.TotalMilliseconds;
        }

        public override bool HasTask(int tid) {
            return taskDic.ContainsKey(tid);
        }

        public override TaskInfo? GetTaskInfo(int tid) {
            if(taskDic.TryGetValue(tid, out TickTask task)) {
                return new TaskInfo {
                    tid = task.tid,
                    delay = task.delay,
                    remainingCount = task.count > 0 ? task.count : -1,
                    status = TaskStatus.Running
                };
            }
            return null;
        }

        public override List<int> GetAllTaskIds() {
            return new List<int>(taskDic.Keys);
        }

        public override int GetTaskCount() {
            return taskDic.Count;
        }

        protected override int GenerateTid() {
            // 使用原子操作优化性能
            int newTid = Interlocked.Increment(ref tid);
            if(newTid == int.MaxValue || newTid < 0) {
                lock(tidLock) {
                    if(tid >= int.MaxValue || tid < 0) {
                        tid = 0;
                    }
                    newTid = Interlocked.Increment(ref tid);
                }
            }

            // 如果ID已存在，使用传统方法查找
            if(taskDic.ContainsKey(newTid)) {
                lock(tidLock) {
                    int startTid = newTid;
                    do {
                        newTid = Interlocked.Increment(ref tid);
                        if(newTid == int.MaxValue || newTid < 0) {
                            tid = 0;
                            newTid = Interlocked.Increment(ref tid);
                        }
                        if(!taskDic.ContainsKey(newTid)) {
                            return newTid;
                        }
                    } while(newTid != startTid);
                }
            }

            return newTid;
        }

        class TickTask {
            public int tid;
            public uint delay;
            public int count;
            public double destTime;
            public Action<int> taskCB;
            public Action<int> cancelCB;

            public double startTime;
            public ulong loopIndex;

            public TickTask(
                int tid,
                uint delay,
                int count,
                double destTime,
                Action<int> taskCB,
                Action<int> cancelCB,
                double startTime) {
                this.tid = tid;
                this.delay = delay;
                this.count = count;
                this.destTime = destTime;
                this.taskCB = taskCB;
                this.cancelCB = cancelCB;
                this.startTime = startTime;

                this.loopIndex = 0;
            }
        }
    }
}


